﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// 与填空题相关的方法。
    /// </summary>
    internal static class QuestionValidateManager
    {
        /// <summary>
        /// [静态构造方法]初始化空白字符数组，以用来进行文本操作。
        /// </summary>
        static QuestionValidateManager()
        {
            blankChars = new char[6];
            blankChars[0] = ' ';
            blankChars[1] = '\t';
            blankChars[2] = '　';//全角空格
            blankChars[3] = '\r';//验证时不需要
            blankChars[4] = '\n';
        }

        private static char[] blankChars;
        /// <summary>
        /// 空白字符数组，用以进行文本操作。
        /// </summary>
        public static char[] BlankChars
        {
            get { return blankChars; }
        }

        /// <summary>
        /// 按照“全部为选择题”的规则来验证整个文档的合法性。
        /// </summary>
        /// <param name="documentText">要验证的文档的全部文本。</param>
        /// <param name="errorMsg">返回的错误消息。</param>
        /// <returns>验证合法会返回真。</returns>
        public static bool ValidateAsTestPaper(string documentText, StringBuilder errorMsg = null)
        {
            //忽略所有空白字符，不能用Trim，因为Trim只去首尾。
            documentText = documentText.Replace("\r\n", "").Replace("\t", "").Replace(" ", "").Replace("　", "");

            //去除文件信息那几行
            if (documentText.StartsWith("＜＜＜信息＞"))
            {
                int indexOfEndTitleTag = documentText.IndexOf("＜信息＞＞＞");
                if (indexOfEndTitleTag < 0)
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append("文档非法，文件信息部分缺少结束标记“＜信息＞＞＞”。请使用自动完成功能在行首添加“信息”并编辑文件信息文本。");
                    }
                }
                else
                {
                    documentText = documentText.Substring(indexOfEndTitleTag + 6);
                }
            }

            string[] separator = new string[2];
            separator[0] = "试题＞＞";
            separator[1] = "〓〓〓〓〓〓";

            string[] answerSeparator = new string[1];
            answerSeparator[0] = "答案＞＞";

            string[] errorSeparator = new string[1];
            errorSeparator[0] = "错项＞＞";

            string[] questionTexts = documentText.Split(separator, StringSplitOptions.RemoveEmptyEntries);

            if (questionTexts.Length <= 0)
            {
                if (errorMsg != null)
                {
                    errorMsg.Append("没有找到任何试题，此文档对于试题演示操作无意义。");
                }
            }

            for (int i = 1; i <= questionTexts.Length; i++)
            {
                string questionText = questionTexts[i - 1];

                //先找出试题文本

                if (questionText.Contains("＜＜＜信息＞") || questionText.Contains("＜信息＞＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“信息”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("标题＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“标题”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("日期＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“日期”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("作者＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“作者”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("缉录＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“缉录”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("辑录＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“辑录”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("电邮＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“电邮”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("备注＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“备注”标签。\r\n", i));
                    }
                }

                if (questionText.StartsWith("答案＞＞") || questionText.StartsWith("错项＞＞") || questionText.StartsWith("解析＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，没有试题文本。试题不能以“答案”、“错项”或“解析”开头。\r\n", i));
                    }
                }
                else
                {
                    string[] questionTitleAndAnswers = questionText.Split(answerSeparator, StringSplitOptions.None);//这里不去除空数组，反正不是按换行符来分的。
                    if (questionTitleAndAnswers.Length <= 0)
                    {
                        if (errorMsg != null)
                        {
                            errorMsg.Append(string.Format("第{0}题非法，没有试题文本，没有答案，也未提供错项。\r\n", i));
                        }
                    }
                    else if (questionTitleAndAnswers.Length == 1)
                    {
                        //为什么要去除呢？因为填空题就是没有答案与错项的——【填充文本】就是答案。
                        //if (errorMsg != null)
                        //{
                        //    errorMsg.Append(string.Format("第{0}题非法，只有试题文本，没有答案，也未提供错项。\r\n", i));
                        //}

                        int fillBlankCount = -1;
                        string questionTitleText = questionTitleAndAnswers[0];

                        StringBuilder questionErrorMsg = new StringBuilder();

                        if (ValidatePairOfBrackets(questionTitleText, ref fillBlankCount, true, questionErrorMsg))
                        {
                            if (fillBlankCount <= 0)
                            {
                                if (errorMsg != null) errorMsg.Append(string.Format("第{0}题非法，试题文本未中没有设置填空项。\r\n", i));
                            }
                        }
                        else
                        {
                            if (errorMsg != null)
                            {
                                errorMsg.Append(string.Format("第{0}题非法，" + questionErrorMsg.ToString(), i));
                            }
                        }
                    }
                    else
                    {
                        //判断答案数目是否符合
                        //先判断题目是否合法，规则如下：
                        //①至少一个括弧对（包含着填充项）。
                        //②括弧要匹配
                        //③有一个填充项就要有一个“答案区域”
                        //④“答案区域”中可以包含解析，但“解析＞＞”字样不能出现在开头。

                        string questionTitleText = questionTitleAndAnswers[0];
                        int fillBlankCount = -1;

                        StringBuilder questionErrorMsg = new StringBuilder();

                        if (ValidatePairOfBrackets(questionTitleText, ref fillBlankCount, true, questionErrorMsg))
                        {
                            if (fillBlankCount != questionTitleAndAnswers.Length - 1)
                            {
                                if (errorMsg != null) errorMsg.Append(string.Format("第{0}题非法，提供的答案数目与试题中的填空项数目不一致。\r\n", i));
                            }
                            else
                            {
                                for (int i2 = 1; i2 < questionTitleAndAnswers.Length; i2++)
                                {
                                    string answerText = questionTitleAndAnswers[i2];
                                    if (answerText.StartsWith("解析＞＞"))
                                    {
                                        if (errorMsg != null) errorMsg.Append(string.Format("第 {0} 题，第 {1} 个填充项未提供答案，或答案解析位置不正确。", i, i2));
                                    }

                                    string[] errorItems = answerText.Split(errorSeparator, StringSplitOptions.RemoveEmptyEntries);
                                    if (errorItems.Length < 2)//第一项是答案，要求至少一个答案、一个备选的错项。
                                    {
                                        if (errorMsg != null) errorMsg.Append(string.Format("第 {0} 题，第 {1} 个填充项未提供答案或未提供备选的错项。\r\n", i, i2));
                                    }
                                    else
                                    {
                                        for (int i3 = 1; i3 < errorItems.Length; i3++)
                                        {
                                            if (errorItems[i3].StartsWith("解析＞＞") || errorItems[i3].Length <= "错项＞＞".Length)
                                            {
                                                if (errorMsg != null) errorMsg.Append(string.Format("第 {0} 题，第 {1} 个填充项的某个错项未提供文本，或错项解析位置不正确。\r\n", i, i2));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (errorMsg != null)
                            {
                                errorMsg.Append(string.Format("第{0}题非法，" + questionErrorMsg.ToString(), i));
                            }
                        }
                    }
                }
            }

            if (string.IsNullOrEmpty(errorMsg.ToString())) return true;

            return false;
        }

        /// <summary>
        /// 用于校验“试题＞＞”文本是否合法。★注意，此时，选择题题干文本不支持多行！！！
        /// </summary>
        /// <param name="sourceText">要校验的文本。</param>
        /// <param name="allowJoinFillBlanks">是否允许两对括弧连续出现。</param>
        /// <param name="errorMsg">错误消息。</param>
        /// <returns>如果合法，返回真。否则返回假。</returns>
        public static bool ValidatePairOfBrackets(string sourceText, ref int fillblankCount, bool allowJoinFillBlanks = true, StringBuilder errorMsg = null, bool allowNoFillBlank = false)
        {
            if (string.IsNullOrEmpty(sourceText))
            {
                if (errorMsg != null)
                {
                    errorMsg.Append("没有文本。");
                }
                return false;
            }

            if (allowNoFillBlank == false)
            {
                if (sourceText.Contains("【") == false || sourceText.Contains("】") == false)
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append("没有答案文本（答案文本应以方括号对【】包围。）");
                    }
                    return false;
                }
            }

            if (sourceText.Contains("试题＞＞"))
            {
                sourceText = sourceText.TrimStart(blankChars);

                if (sourceText.StartsWith("试题＞＞"))
                {
                    sourceText = sourceText.Substring(4);
                }
            }

            if (sourceText.Contains("解析＞＞"))
            {
                if (errorMsg != null)
                    errorMsg.Append("“答案”之前不应该有“解析”。\r\n");
                return false;
            }

            List<int> startBracketIndexesList = new List<int>();
            List<int> endBracketIndexesList = new List<int>();

            for (int i = 0; i < sourceText.Length; i++)
            {
                char c = sourceText[i];
                if (c == '【')
                {
                    startBracketIndexesList.Add(i);
                }
                else if (c == '】')
                {
                    endBracketIndexesList.Add(i);
                }
            }

            #region 对两个列表比较之前，先保证两个列表成员数目相等。

            if (allowJoinFillBlanks == false)
            {
                if (startBracketIndexesList.Count <= 0 || endBracketIndexesList.Count <= 0)
                {
                    if (errorMsg != null)
                        errorMsg.Append("试题无填充项。\r\n");
                    return false;
                }
            }

            if (startBracketIndexesList.Count < endBracketIndexesList.Count)
            {
                for (int i = 0; i < (endBracketIndexesList.Count - startBracketIndexesList.Count); i++)
                {
                    startBracketIndexesList.Add(-1);
                }

                if (errorMsg != null)
                    errorMsg.Append("括弧数目不等。\r\n");
                return false;
            }
            else if (startBracketIndexesList.Count > endBracketIndexesList.Count)
            {
                for (int i = 0; i < (startBracketIndexesList.Count - endBracketIndexesList.Count); i++)
                {
                    endBracketIndexesList.Add(-1);
                }

                if (errorMsg != null)
                    errorMsg.Append("括弧数目不等。\r\n");
            }
            else
            {
                fillblankCount = startBracketIndexesList.Count;
            }
            #endregion

            for (int i = 0; i < startBracketIndexesList.Count; i++)
            {
                if (startBracketIndexesList[i] == -1 || endBracketIndexesList[i] == -1)
                {
                    if (errorMsg != null)
                        errorMsg.Append("首尾括弧不匹配。\r\n");
                    return false;
                }

                if (startBracketIndexesList[i] >= endBracketIndexesList[i])
                {
                    if (errorMsg != null)
                        errorMsg.Append("首尾括弧不匹配。\r\n");
                    return false;
                }

                if (startBracketIndexesList[i] + 1 == endBracketIndexesList[i])
                {
                    //首尾括弧之间没有答案文本
                    if (errorMsg != null)
                        errorMsg.Append("首尾括弧之间没有答案文本。\r\n");
                    return false;
                }
            }

            for (int i = 0; i < startBracketIndexesList.Count - 1; i++)
            {
                //前一个尾括弧必须在后一个首括弧之前
                if (endBracketIndexesList[i] >= startBracketIndexesList[i + 1])
                {
                    if (errorMsg != null)
                        errorMsg.Append("括弧对交错。\r\n");
                    return false;
                }

                if (allowJoinFillBlanks == false)
                {
                    if (endBracketIndexesList[i] + 1 == startBracketIndexesList[i + 1])
                    {
                        //前一个尾括弧正好在后一个首括弧前1位——两个填空没必要连在一起——这时候做成一个填空就可以了。
                        if (errorMsg != null)
                            errorMsg.Append("两个填充项连在一起，这没必要。\r\n");
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 用于演示前的验证。规则比选择题宽松得多。
        /// </summary>
        /// <param name="documentText"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static bool ValidateForPresentation(string documentText, StringBuilder errorMsg = null)
        {
            //忽略所有空白字符，不能用Trim，因为Trim只去首尾。
            documentText = documentText.Replace("\r\n", "").Replace("\t", "").Replace(" ", "").Replace("　", "");

            //去除文件信息那几行
            if (documentText.StartsWith("＜＜＜信息＞"))
            {
                int indexOfEndTitleTag = documentText.IndexOf("＜信息＞＞＞");
                if (indexOfEndTitleTag < 0)
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append("文档非法，文件信息部分缺少结束标记“＜信息＞＞＞”。请使用自动完成功能在行首添加“信息”并编辑文件信息文本。或者去除这部分。");
                    }
                }
                else
                {
                    documentText = documentText.Substring(indexOfEndTitleTag + 6);
                }
            }

            string[] separator = new string[2];
            separator[0] = "试题＞＞";
            separator[1] = "〓〓〓〓〓〓";

            string[] answerSeparator = new string[1];
            answerSeparator[0] = "答案＞＞";

            string[] errorSeparator = new string[1];
            errorSeparator[0] = "错项＞＞";

            string[] questionTexts = documentText.Split(separator, StringSplitOptions.RemoveEmptyEntries);

            if (questionTexts.Length <= 0)
            {
                if (errorMsg != null)
                {
                    errorMsg.Append("没有找到任何试题，此文档对于试题演示操作无意义。");
                }
            }

            for (int i = 1; i <= questionTexts.Length; i++)
            {
                string questionText = questionTexts[i - 1];

                //先找出试题文本

                if (questionText.Contains("＜＜＜信息＞") || questionText.Contains("＜信息＞＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“信息”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("标题＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“标题”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("日期＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“日期”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("作者＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“作者”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("缉录＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“缉录”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("辑录＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“辑录”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("电邮＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“电邮”标签。\r\n", i));
                    }
                }

                if (questionText.Contains("备注＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，试题文本中不能包含“备注”标签。\r\n", i));
                    }
                }

                if (questionText.StartsWith("答案＞＞") || questionText.StartsWith("错项＞＞") || questionText.StartsWith("解析＞＞"))
                {
                    if (errorMsg != null)
                    {
                        errorMsg.Append(string.Format("第{0}题非法，没有试题文本。试题不能以“答案”、“错项”或“解析”开头。\r\n", i));
                    }
                }
                else
                {
                    string[] questionTitleAndAnswers = questionText.Split(answerSeparator, StringSplitOptions.None);//这里不去除空数组，反正不是按换行符来分的。
                    if (questionTitleAndAnswers.Length <= 0)
                    {
                        if (errorMsg != null)
                        {
                            errorMsg.Append(string.Format("第{0}题非法，没有试题文本，没有答案，也未提供错项。\r\n", i));
                        }
                    }
                    else if (questionTitleAndAnswers.Length == 1)
                    {
                        //没有答案项和错项，是填空题

                        int fillBlankCount = -1;
                        string questionTitleText = questionTitleAndAnswers[0];

                        StringBuilder questionErrorMsg = new StringBuilder();

                        if (ValidatePairOfBrackets(questionTitleText, ref fillBlankCount, true, questionErrorMsg, true) == false)
                        {
                            if (errorMsg != null)
                            {
                                errorMsg.Append(string.Format("第{0}题非法，" + questionErrorMsg.ToString(), i));
                            }
                        }
                    }
                    else//有标题、有答案项
                    {
                        //判断答案数目是否符合
                        //先判断题目是否合法，规则如下：
                        //①以括弧对包含着填充项。
                        //②括弧要匹配
                        //③有一个填充项就要有一个“答案区域”
                        //④“答案区域”中可以包含解析，但“解析＞＞”字样不能出现在开头。

                        string questionTitleAndAnswerText = questionTitleAndAnswers[0];
                        int fillBlankCount = -1;//标题中填充项（指标题文本中用一对方括号括起来的文本片段）的数目

                        StringBuilder questionErrorMsg = new StringBuilder();

                        if (ValidatePairOfBrackets(questionTitleAndAnswerText, ref fillBlankCount, true, questionErrorMsg, false))//不允许没有填充项。
                        {
                            //标题中有填充项、标题外提供了答案项（错项附属于答案项，一个答案项可备零到多个错项，但主观题的答案项不应提供错项）。
                            if (fillBlankCount != questionTitleAndAnswers.Length - 1)
                            {
                                if (fillBlankCount != 0)
                                {
                                    //当填充项与提供的答案数目不一致时，要么没有答案（填空题）；要么就是出错了。
                                    //此分支questionTitleAndAnswers.Length > 1
                                    if (errorMsg != null) errorMsg.Append(string.Format("第{0}题非法，提供了答案，但提供的答案数目与试题中的填充项数目不一致。\r\n", i));
                                }
                                else
                                {
                                    //当提供了答案和解析时，如果填空项为0，则说明这是一主观题。
                                    //主观题也可以有多个答案项和解析。但主观题中不应该有“错项”。
                                    bool hasErrorItem = false;
                                    for (int i4 = 1; i4 < questionTitleAndAnswers.Length; i4++)
                                    {
                                        if (questionTitleAndAnswers[i4].Contains("错项＞＞"))
                                        {
                                            hasErrorItem = true;
                                            break;
                                        }
                                    }

                                    if (hasErrorItem)
                                    {
                                        if (errorMsg != null) errorMsg.Append(string.Format("第{0}题非法，没有填充项的试题应是主观题，但主观题的答案中不应出现“错项＞＞”字样。\r\n", i));
                                    }
                                }
                            }
                            else//填充项数目正好等于提供的答案的数目。这应该是选择题。
                            {
                                for (int i2 = 1; i2 < questionTitleAndAnswers.Length; i2++)
                                {
                                    string answerText = questionTitleAndAnswers[i2];

                                    if (answerText.StartsWith("解析＞＞"))
                                    {
                                        if (errorMsg != null) errorMsg.Append(string.Format("第 {0} 题，第 {1} 个填充项未提供答案，或答案解析位置不正确。", i, i2));
                                    }

                                    if (fillBlankCount > 0)//填空有填充项但无答案项、错项；判断没有填充项、没有答案项。
                                    {
                                        string[] errorItems = answerText.Split(errorSeparator, StringSplitOptions.RemoveEmptyEntries);
                                        if (errorItems.Length == 1)//要么是填空、判断（没有答案项）；要么是选择（至少一个答案、一个错项）。
                                        {
                                            if (errorMsg != null) errorMsg.Append(string.Format("第 {0} 题，第 {1} 个填充项未提供答案或未提供备选的错项。\r\n", i, i2));
                                        }
                                        else
                                        {
                                            for (int i3 = 1; i3 < errorItems.Length; i3++)
                                            {
                                                if (errorItems[i3].StartsWith("解析＞＞") || errorItems[i3].Length <= "错项＞＞".Length)
                                                {
                                                    if (errorMsg != null) errorMsg.Append(string.Format("第 {0} 题，第 {1} 个填充项的某个错项未提供文本，或错项解析位置不正确。\r\n", i, i2));
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (questionTitleAndAnswerText.Contains("材料＞＞") == false && errorMsg != null)
                            {
                                errorMsg.Append(string.Format("第{0}题非法，" + questionErrorMsg.ToString(), i));
                            }
                        }
                    }
                }
            }

            if (string.IsNullOrEmpty(errorMsg.ToString())) return true;

            return false;
        }
    }
}
